/*****************************************************************************
*
*  PROJECT:     SlipStream
*               (Main Code File)
*  LICENSE:     GNU General Public License v3 - http://www.gnu.org/licenses/gpl.html

*  DEVELOPERS:  Philip Farquharson <B00137077@studentmail.uws.ac.uk>
*				John Gray <B00145524@studentmail.uws.ac.uk>		
*				Zohaib Bhatti <B00104280@studentmail.uws.ac.uk>
*				Euan Hislop <B00138636@studentmail.uws.ac.uk>
*
*****************************************************************************/

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <windowsx.h>
#include <gl\gl.h>

#include "CGame.h"

#include "CKeyBindManager.h"

#define _CRT_SECURE_NO_WARNINGS

#pragma comment( lib, "opengl32.lib" )

#pragma comment( lib, "cg.lib" )
#pragma comment( lib, "cggl.lib" )	
#pragma comment( lib, "ode_singled.lib" )	

CGame g_Game;

HGLRC           hRC = NULL;						// rendering context
HDC             hDC = NULL;						// private gdi context
HWND            hWnd = NULL;					// window handle
HINSTANCE       hInstance;						// application instance

bool active = true;								// window active, true by default
bool fullscreen = false;						// fullscreen, false by default

LRESULT	CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);											// message handler declaration
GLvoid KillGLWindow(GLvoid);																	// properly destroy the window
BOOL createGLWindow(char* title, int width, int height, int bits, bool fullscreenflag);			// create the window

#define WIDTH		800
#define HEIGHT		600

int WINAPI WinMain(	HINSTANCE	hInstance,				// instance
				   HINSTANCE	hPrevInstance,			// previous instance
				   LPSTR		lpCmdLine,				// command line params
				   int		nCmdShow)					// show state
{
	const int BITSPERPIXEL = 32;
	char windowTitle[] = "SlipStream - Loading";

	MSG	msg;									// windows message struct
	bool done = false;							// bool to exit the main loop

	// attempt to load settings
	try
	{
		ifstream ifile(SETTINGS_FILENAME);
		if (ifile.is_open())
		{
			std::string str;
			getline(ifile, str);
			int val = atoi(&str.c_str()[0]);
			fullscreen = val == 1 ? true : false;

			if ( val != 0 && val != 1 ) // invalid value
				throw "Invalid Settings File";
		}
		else
			throw "File Not Created";
	}
	catch (...)
	{
		fullscreen = MessageBox(hWnd, "Would you like to go full screen? \n\n Your setting will be saved for next time.", "Fullscreen?", MB_YESNO|MB_ICONEXCLAMATION) == IDYES ? true: false;
		ofstream file;
		file.open(SETTINGS_FILENAME);
		file << fullscreen ? '1' : '0';
		file.close();
	}

	if (!createGLWindow(windowTitle,WIDTH,HEIGHT,BITSPERPIXEL,fullscreen))
	{
		return 0;								// quit if the window was not created
	}
	//fclose(file);
	while (!done)								// loop until done is true
	{

		while (PeekMessage(&msg,NULL,0,0,PM_REMOVE))	// message waiting?
		{
			if (msg.message==WM_QUIT)				// recieved a quit message?
			{
				done = true; // exit the main loop
			}
			else								// if not, deal with the messages
			{
				TranslateMessage(&msg);				
				DispatchMessage(&msg);				
			}
		}

		int iResult = g_Game.mainLoop(); // result returned from our CGame instance main loop

		if (iResult == CGame::RESULT_EXIT_GAME)
		{
			done = true;
			continue;
		}	
		else if (iResult == CGame::RESULT_CONTINUE_GAME)
		{
			active = true;
		}
		if (active)//&& GetActiveWindow() == hWnd)						// program active?
		{
			g_Game.renderGLScene();		// render the scene
			SwapBuffers(hDC);			// swap our buffers for double buffering
		}

		if (!active || GetActiveWindow() != hWnd)
		{
			Sleep(25);
		}
	}
	
	// shutdown
	g_Game.shutDown(); // clean up
	KillGLWindow(); // kill the window

	return ((int)msg.wParam);			// exit
}


BOOL createGLWindow(char* title, int width, int height, int bits, bool fullscreenflag)
{
	GLuint		PixelFormat;				// results
	WNDCLASS	wc;							// window class struct
	DWORD		dwExStyle;					// window extended style
	DWORD		dwStyle;					// window style
	RECT WindowRect;						// upper left & lower right values
	WindowRect.left = (long)0;				// left as 0
	WindowRect.right = (long)width;			// right as width
	WindowRect.top = (long)0;					// top as 0
	WindowRect.bottom = (long)height;			// bottom as height
	fullscreen = fullscreenflag;				// set the fullscreen flag
	hInstance		= GetModuleHandle(NULL);	// grab an instance for this window
	wc.style		= CS_HREDRAW | CS_VREDRAW | CS_OWNDC;	// redraw on move, and own DC for window
	wc.lpfnWndProc		= (WNDPROC) WndProc;				// WndProc handles messages
	wc.cbClsExtra		= 0;						// no extra data
	wc.cbWndExtra		= 0;						// no extra data
	wc.hInstance		= hInstance;				// set the instance
	wc.hIcon		= LoadIcon(NULL, IDI_WINLOGO);	// load the default icon
	wc.hCursor		= LoadCursor(NULL, IDC_ARROW);	// load the arrow pointer
	wc.hbrBackground	= NULL;						// no background required for OpenGL
	wc.lpszMenuName		= NULL;						// no menu required for OpenGL
	wc.lpszClassName	= "OpenGL";					// window class name


	if (!RegisterClass(&wc))						// attempt to register the window class
	{
		MessageBox(NULL, "Failed To Register The Window Class.", "ERROR", MB_OK|MB_ICONEXCLAMATION);
		return false;								// return false, which exits
	}

	if (fullscreen)									// do we want full screen?
	{
		DEVMODE dmScreenSettings;								// device mode
		memset(&dmScreenSettings,0,sizeof(dmScreenSettings));	// clear device mode structure
		dmScreenSettings.dmSize	= sizeof(dmScreenSettings);		// size of device mode structure
		dmScreenSettings.dmPelsWidth = width;					// screen width
		dmScreenSettings.dmPelsHeight = height;					// screen height
		dmScreenSettings.dmBitsPerPel = bits;					// bpp
		dmScreenSettings.dmFields = DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;

		// try to set the mode of the window
		if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
		{
			// if setting the mode fails, offer the user the choice to stay in windowed mode
			if (MessageBox(hWnd, "Full screen is not supported by Your Video Card. \n\nUse Windowed Mode Instead?", "Fullscreen Unsupported", MB_YESNO|MB_ICONEXCLAMATION) == IDYES)
			{
				fullscreen = false;				// lets go back to windowed mode
			}
			else
			{
				return false;					// exit
			}
		}
	}

	if (fullscreen)								// still in fullscreen?
	{
		dwExStyle = WS_EX_APPWINDOW;			// window extended style
		dwStyle = WS_POPUP;						// window style
		ShowCursor(false);						// hide the mouse cursor
	}
	else
	{
		dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;	// window extended style
		dwStyle = WS_OVERLAPPEDWINDOW;					// window style
		ShowCursor(true);								// show the mouse cursor
	}

	AdjustWindowRectEx(&WindowRect, dwStyle, false, dwExStyle);	// adjust the window size to the real size

	// use winapi to create the window with our details above
	if (!(hWnd=CreateWindowEx(	dwExStyle,
		"OpenGL",
		title,
		WS_CLIPSIBLINGS |
		WS_CLIPCHILDREN |
		dwStyle,
		0, 0,
		WindowRect.right-WindowRect.left,
		WindowRect.bottom-WindowRect.top,
		NULL,
		NULL,
		hInstance,
		NULL)))
	{
		KillGLWindow();							// reset display
		MessageBox(NULL, "Window Creation Error.", "ERROR", MB_OK|MB_ICONEXCLAMATION);
		return false;							// Return false
	}


	static	PIXELFORMATDESCRIPTOR pfd =		// pixel format descriptor
	{
		sizeof(PIXELFORMATDESCRIPTOR),		// size
		1,									// version
		PFD_DRAW_TO_WINDOW |				// must support window
		PFD_SUPPORT_OPENGL |				// must support opengl
		PFD_DOUBLEBUFFER,					// must support double buffering
		PFD_TYPE_RGBA,						// request rgba format
		bits,								// colour depth
		0, 0, 0, 0, 0, 0,					// colour bits ignored
		0,									// no alpha
		0,									// no shift bit
		0,									// no accumulation buffer
		0, 0, 0, 0,							// accumulation bits ignored
		16,									// 16bit depth buffer
		0,									// no stencil buffer
		0,									// no auxillary buffer
		PFD_MAIN_PLANE,						// main drawing layer
		0,									// reserved
		0, 0, 0								// layer masks ignored
	};

	if (!(hDC=GetDC(hWnd)))					// got a device context?
	{
		KillGLWindow();						// reset display
		MessageBox(NULL, "Can't Create An OpenGL Device Context.", "ERROR", MB_OK|MB_ICONEXCLAMATION);
		return false;						
	}

	if (!(PixelFormat=ChoosePixelFormat(hDC,&pfd))) // got a matcing pixel format?
	{
		KillGLWindow();							// reset display
		MessageBox(NULL, "Can't Find A Suitable PixelFormat.", "ERROR", MB_OK|MB_ICONEXCLAMATION);
		return false;
	}

	if(!SetPixelFormat(hDC,PixelFormat,&pfd))
	{
		KillGLWindow();
		MessageBox(NULL, "Can't Set The PixelFormat.", "ERROR", MB_OK|MB_ICONEXCLAMATION);
		return false;							
	}

	if (!(hRC=wglCreateContext(hDC)))
	{
		KillGLWindow();
		MessageBox(NULL, "Can't Create A GL Rendering Context.", "ERROR", MB_OK|MB_ICONEXCLAMATION);
		return false;							
	}

	if(!wglMakeCurrent(hDC,hRC))
	{
		KillGLWindow();	
		MessageBox(NULL, "Can't Activate The GL Rendering Context.", "ERROR", MB_OK|MB_ICONEXCLAMATION);
		return false;							
	}

	ShowWindow(hWnd,SW_SHOW);					// show window
	SetForegroundWindow(hWnd);					// slightly higher priority
	SetFocus(hWnd);								// sets the keyboard focus to this window	

	if (!g_Game.init(hDC)) // if we couldnt initialize CGame
	{
		KillGLWindow();							// reset display
		MessageBox(NULL, "Initialization Failed.", "ERROR", MB_OK|MB_ICONEXCLAMATION);
		return false;							
	}

	g_Game.hWnd = hWnd;							 // store our window handle in our CGame instance
	g_Game.resizeGLScene(width, height);		// resize our CGame instance to our new dimensions
	SetWindowText(hWnd, "SlipStream");
	return true;
}

LRESULT CALLBACK WndProc(HWND	hWnd,
						 UINT	uMsg,
						 WPARAM	wParam,
						 LPARAM	lParam)
{
	active=true;

	// store if we have focus or not in our keybind manager
	if (GetActiveWindow() == hWnd && g_Game.m_KeyBindManager.getHasFocus() == false)
		g_Game.m_KeyBindManager.setHasFocus(true);
	else if (GetActiveWindow() != hWnd && g_Game.m_KeyBindManager.getHasFocus() == true)
		g_Game.m_KeyBindManager.setHasFocus(false);

	// switch to our windows message
	switch (uMsg)
	{
	case WM_ACTIVATE:
		if (!HIWORD(wParam))
			active=true;
		else
			active=false;
		return 0;
	case WM_SYSCOMMAND:
		switch (wParam)
		{
		case SC_SCREENSAVE:
		case SC_MONITORPOWER:
			return 0;
		}
		break;
	case WM_CLOSE:
		PostQuitMessage(0);
		return 0;
	case WM_SIZE:
		{
			// disable resizing the window when in windowed mode
			RECT rect;
			GetWindowRect(hWnd, &rect);

			int width = rect.right - rect.left;
			int height = rect.bottom - rect.top;

			if (width > WIDTH + 50 || height > HEIGHT + 50)
			{
				ShowWindow(hWnd, 1);
				SetWindowPos(hWnd, NULL, rect.left, rect.top, WIDTH, HEIGHT, SWP_NOMOVE|SWP_NOZORDER);
				return 0;
			}
			g_Game.resizeGLScene(LOWORD(lParam),HIWORD(lParam));
			return 0;
		}
	}

	return DefWindowProc(hWnd,uMsg,wParam,lParam);
}

GLvoid KillGLWindow(GLvoid)
{
	if (fullscreen)
	{
		ChangeDisplaySettings(NULL,0);
		ShowCursor(true);
	}

	if (hRC)
	{
		if (!wglMakeCurrent(NULL,NULL))
		{
			MessageBox(NULL, "Release Of DC And RC Failed.", "SHUTDOWN ERROR", MB_OK|MB_ICONINFORMATION);
		}
		if (!wglDeleteContext(hRC))
		{
			MessageBox(NULL, "Release Rendering Context Failed.", "SHUTDOWN ERROR", MB_OK|MB_ICONINFORMATION);
		}
		hRC = NULL;								
	}

	if (hDC && !ReleaseDC(hWnd,hDC))
	{
		MessageBox(NULL, "Release Device Context Failed.", "SHUTDOWN ERROR" ,MB_OK|MB_ICONINFORMATION);
		hDC = NULL;								
	}

	if (hWnd && !DestroyWindow(hWnd))
	{
		MessageBox(NULL, "Could Not Release hWnd.", "SHUTDOWN ERROR", MB_OK|MB_ICONINFORMATION);
		hWnd = NULL;
	}

	if (!UnregisterClass("OpenGL",hInstance))
	{
		MessageBox(NULL, "Could Not Unregister Class." ,"SHUTDOWN ERROR", MB_OK|MB_ICONINFORMATION);
		hInstance = NULL;							
	}
}
